__author__ = 'Brandon Corfman'
from abc import ABCMeta, abstractmethod
import term

class Action():
    __metaclass__ = ABCMeta

    @abstractmethod
    def precondition(self, agent, board, others):
        """
        agent: HumanAgent or ComputerAgent
        board: Board
        others: set of other agents in the game (besides the current agent)
        Must return a value (rank) between 0.0 (action is unsuitable) and 1.0 (action is perfect)
        """
        pass

    @abstractmethod
    def execute(self, agent, board, others):
        """
        agent: HumanAgent or ComputerAgent
        board: Board
        others: set of other agents in the game (besides the current agent)
        Returns nothing.
        """
        pass

    @abstractmethod
    def is_flippable(self):
        """
        Returns a True or False depending on whether the path card can be flipped 180 degrees to obtain
        a new orientation.
        """
        pass

class BrokenTool(Action):
    def __init__(self, state):
        self.state = state
        self.other = None

    def __repr__(self):
        return 'BrokenTool: %s' % self.state

    def __eq__(self, other):
        return self.state == other.state

    def __ne__(self, other):
        return self.state != other.state

    def precondition(self, agent, board, others):
        rank, self.other = agent.decide_broken_tool(board, self.state, others)
        return rank

    def execute(self, agent, board, others):
        agent.notify('BrokenTool {0} {1} {2}'.format(agent.parent.name, self.other.name, self.state))
        self.other.add_state(self.state)

    def is_flippable(self):
        return False

class RepairOne(Action):
    def __init__(self, state):
        self.state = state

    def __repr__(self):
        return 'RepairOne: %s' % self.state
    
    def __eq__(self, other):
        return self.state == other.state

    def __ne__(self, other):
        return self.state != other.state

    def precondition(self, agent, board, others):
        all_players = [agent.parent]
        all_players.extend(others)
        relevant_players = [p for p in all_players if (self.state in p.get_states())]

        if not relevant_players:
            agent.display_repair_one_precond_failure(self.state)
            return 0.0
        rank, self.player, self.state = agent.decide_repair_one(board, self, others)
        return rank

    def execute(self, agent, board, others):
        self.player.remove_state(self.state)
        agent.notify('RepairOne {0} {1} {2}'.format(agent.parent.name, self.player.name, self.state))
        if self.player.agent == agent:
            print("\nYour {0} is now fixed!\n".format(self.state))
        else:
            if self.player.name[-1] == 's': # if name ending in s, use apostrophe only (yes, I'm anal.)
                print("\n{0}' {1} is now fixed!\n".format(self.player, self.state))
            else:
                print("\n{0}'s {1} is now fixed!\n".format(self.player, self.state))
        term.wait_for_enter_key()

    def is_flippable(self):
        return False

class RepairAny(Action):
    def __init__(self, state1, state2):
        self.state = None
        self.state1 = state1
        self.state2 = state2

    def __repr__(self):
        return 'RepairAny: %s or %s' % (self.state1, self.state2)

    def __eq__(self, other):
        return self.state1 == other.state and self.state2 == other.state2

    def __ne__(self, other):
        return self.state1 != other.state1 or self.state2 != other.state2

    def precondition(self, agent, board, others):
        all_players = [agent.parent]
        all_players.extend(others)
        relevant_players = [p for p in all_players if (self.state1 in p.get_states() or
                                                       self.state2 in p.get_states())]

        if not relevant_players:
            agent.display_repair_any_precond_failure(self.state1, self.state2)
            return 0.0
        rank, self.player, self.state = agent.decide_repair_any(board, self, others)
        return rank

    def execute(self, agent, board, others):
        self.player.remove_state(self.state)
        agent.notify('RepairAny {0} {1} {2}'.format(agent.parent.name, self.player.name, self.state))
        if self.player.agent == agent:
            print("\nYour {0} is now fixed!\n".format(self.state))
        else:
            if self.player.name[-1] == 's': # if name ending in s, use apostrophe only (yes, I'm anal)
                print("\n{0}' {1} is now fixed!\n".format(self.player, self.state))
            else:
                print("\n{0}'s {1} is now fixed!\n".format(self.player, self.state))
        term.wait_for_enter_key()
        
    def is_flippable(self):
        return False

class RockFall(Action):
    def __eq__(self, other):
        return True

    def __ne__(self, other):
        return False

    def __repr__(self):
        return 'RockFall'

    def precondition(self, agent, board, others):
        rank, self.row, self.col = agent.decide_rockfall(board, self)
        return rank

    def execute(self, agent, board, others):
        agent.notify('RockFall {0} {1} {2}'.format(agent.parent.name, self.row, self.col))
        board.path[self.row][self.col] = None

    def is_flippable(self):
        return False

class Peek(Action):
    def __init__(self):
        self.pos = None

    def __eq__(self, other):
        return True

    def __ne__(self, other):
        return False

    def __repr__(self):
        return 'Peek'
    
    def precondition(self, agent, board, others):
        rank, self.pos = agent.decide_peek(board, self)
        return rank

    def execute(self, agent, board, others):
        agent.notify('Peek {0} {1}'.format(agent.parent.name, self.pos))
        agent.reveal_goal(board, self.pos)

    def is_flippable(self):
        return False

class Discard(Action):
    def __init__(self, idx):
        self.idx = idx

    def __eq__(self, other):
        return self.idx == other.idx

    def __ne__(self, other):
        return self.idx != other.idx

    def __repr__(self):
        return 'Discard'

    def precondition(self, agent, board, others):
        rank, self.idx = agent.decide_discard(board, self)
        return rank

    def execute(self, agent, board, others):
        agent.notify('Discard {0} {1}'.format(agent.parent.name, self.idx))
        agent.hand.pop(self.idx)

    def is_flippable(self):
        return False
